#ifndef DRIVER_CPP
#define DRIVER_CPP

/* Driver
*************************************************************************************
*
*	Collects input from user and passes it to a Transaction.
*
*************************************************************************************
*
*   Credits
*
*		Richard Rios
*		Aaron Solomon
*
*************************************************************************************
*/
	#include <iostream>
	#include <cstdio>
	#include <string>

	#include "Inventory.h"
	#include "Transaction.h"

	using namespace std;
/*
*************************************************************************************/

struct Driver {
public:
	static void execute() {
	/*
	*
	*   Description
	*   -----------------------
	*
	*       Begins the driver's input collection and so on
	*
	*   Takes
	*   -----------------------
	*
	*       nothing
	*
	*   Returns
	*   -----------------------
	*
	*       nothing
	*
	*/
		Inventory* inventory;
		Transaction* transaction;

		inventory = loadInventory("Invent.dat");

		string userInput;

		do {
			transaction = startNewTransaction(inventory, "Receipt.out");

			while (getUserInput(userInput)) {
				try {
					transaction->addItem(getProductNumber(userInput), getProductQuantity(userInput));
				} //try
				catch (int) {
					cout << "Invalid Input" << endl;
				} //catch
			} //while

			transaction->endTransaction();
			delete transaction;
		} while (getUserStartNewTransaction());

		delete inventory;
	} //execute
private:
	static const int INVALID_PRODUCT_NUMBER = 1;

	static bool getUserInput(string& userInput) {
	/*
	*
	*   Description
	*   -----------------------
	*
	*       Retrieves input from user and stores it into string
	*
	*   Takes
	*   -----------------------
	*
	*       nothing
	*
	*   Returns
	*   -----------------------
	*
	*       bool
	*
	*			-	true if user inputted something
	*			-	false if user inputted 0 (exit transaction)
	*
	*/
		cout << "Enter item <Product_number> <quantity>: ";
		getline(cin, userInput);

		return (userInput[0] != '0' || userInput.length() > 1);
	} //getUserInput

	static int getProductNumber(string& userInput) {
	/*
	*
	*   Description
	*   -----------------------
	*
	*       Parses userInput for product number
	*
	*		Throws exception if product number is invalid
	*
	*   Takes
	*   -----------------------
	*
	*       nothing
	*
	*   Returns
	*   -----------------------
	*
	*       int
	*
	*			-	product number
	*
	*/
		unsigned int productNumber = 0;
		unsigned int numberStart = 0;
		unsigned int userInputLength = userInput.length();
		int numberSize = 0;

		while (userInput[numberStart] == ' ' || userInput[numberStart] == '\t' && numberStart < userInputLength - 5) ++numberStart;

		for (numberSize = 0; userInput[numberStart + numberSize] != ' ' && userInput[numberStart + numberSize] != '\t' && numberStart + numberSize < userInputLength; ++numberSize) {
			if(userInput[numberStart + numberSize]  > '9' || userInput[numberStart + numberSize] < '0') throw(INVALID_PRODUCT_NUMBER);

			productNumber = productNumber*10 + (int)(userInput[numberStart + numberSize] - '0');
		} //for

		if (numberSize != 5) throw(INVALID_PRODUCT_NUMBER);

		return productNumber;
	} //getProductNumber

	static int getProductQuantity(string& userInput) {
	/*
	*
	*   Description
	*   -----------------------
	*
	*       Parses userInput for product quantity
	*
	*		Throws exception if quantity is invalid
	*
	*   Takes
	*   -----------------------
	*
	*       nothing
	*
	*   Returns
	*   -----------------------
	*
	*       int
	*
	*			-	product quantity
	*
	*/
		unsigned int productAmount = 0;
		unsigned int userInputLength = userInput.length();

		unsigned int i = 0;
		while (userInput[i] == ' ' || userInput[i] == '\t' && i < userInputLength) ++i;
		while (userInput[i] != ' ' && userInput[i] != '\t' && i < userInputLength) ++i;
		while (userInput[i] == ' ' || userInput[i] == '\t' && i < userInputLength) ++i;

		for(; userInput[i] >= '0' && userInput[i] <= '9' && i < userInputLength; ++i) {
			productAmount = productAmount*10 + userInput[i] - '0';
		} //for

		while (userInput[i] == ' ' || userInput[i] == '\t' && i < userInputLength) ++i;

		if (i != userInputLength || productAmount > 100 || productAmount <= 0) throw(INVALID_PRODUCT_NUMBER);

		return productAmount;
	} //getProductQuantity

	static bool getUserStartNewTransaction() {
	/*
	*
	*   Description
	*   -----------------------
	*
	*       Asks the user if they want to start a new transaction.
	*
	*   Takes
	*   -----------------------
	*
	*       nothing
	*
	*   Returns
	*   -----------------------
	*
	*       bool
	*
	*			-	true means start new transaction
	*			-	false means don't start new transaction
	*
	*/
		string userInput = "Y";

		do {
			if ((userInput[0] != 'Y' && userInput[0] != 'N') || userInput.length() != 1) {
				cout << "Invalid input" << endl;
			} //if
			
			cout << "Start New Transaction [Y,N]?: ";
			getline(cin, userInput);
			userInput[0] = toupper(userInput[0]);

		} while((userInput[0] != 'Y' && userInput[0] != 'N') || userInput.length() != 1);
	
		return (userInput[0] == 'Y');
	} //getUserStartNewTransaction

	static Inventory* loadInventory(char* filename) {
	/*
	*
	*   Description
	*   -----------------------
	*
	*       Loads inventory from a file
	*
	*		Exits** if the inventory failed to load
	*
	*   Takes
	*   -----------------------
	*
	*       nothing
	*
	*   Returns
	*   -----------------------
	*
	*       Inventory*
	*
	*			loaded inventory
	*
	*/
		Inventory* inventory;

		/*
		*	Attempt to load inventory
		*/
		try {
			inventory = new Inventory(filename);
		} //try
		catch(int) {
			cout << "a *valid* " << filename << " file must exist with this program in order to work" << endl;
			cout << "Press Any Key to Exit" << endl;
			getchar();
			exit(1);
		} //catch

		return inventory;
	} //loadInventory

	static Transaction* startNewTransaction(Inventory* inventory, char* filename) {
	/*
	*
	*   Description
	*   -----------------------
	*
	*       Starts a new transaction given an inventory and output file for receipt
	*
	*   Takes
	*   -----------------------
	*
	*       Inventory* inventory
	*
	*			-	inventory to use for transactions
	*
	*		char* filename
	*
	*			-	filename to output receipt to
	*
	*   Returns
	*   -----------------------
	*
	*       Transaction*
	*
	*			-	used to handle transactions
	*
	*/
		Transaction* transaction;

		/*
		*	Attempt to start transaction
		*/
		try {
			transaction = new Transaction(filename, inventory);
		} //try
		catch (int) {
			cout << filename << " was unable to be opened. Admin privileges may be necessary." << endl;
			cout << "Press Any Key to Exit" << endl;
			getchar();
			exit(1);
		} //catch

		return transaction;
	} //startNewTransaction
}; //Driver

#endif